#pragma once
#include "logger.hpp"
#include <sqlite3.h>
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <atomic>
#include <unistd.h>
#include <sys/stat.h>
#include <fstream>
#include <cstdio>
namespace mq_zxws
{
    class SqliteHelper
    {
        typedef int (*SqliteCallback)(void *, int, char **, char **);

    public:
        SqliteHelper(const std::string &filename) : _filename(filename) {}
        bool open(int flag = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_filename.c_str(), &_handler, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | flag, nullptr);
            if (ret != SQLITE_OK)
            {
                ELOG("连接失败 ：%s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        bool exec(const std::string &sql, SqliteCallback cb, void *args)
        {
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, args, nullptr);
            if (ret != SQLITE_OK)
            {
                ELOG("执行失败 ：%s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        void close()
        {
            if (_handler)
            {
                sqlite3_close_v2(_handler);
            }
        }

    private:
        std::string _filename;
        sqlite3 *_handler;
    };

    class StrHelper
    {
    public:
        static size_t splite(const std::string &s, const std::string &sep, std::vector<std::string> &result)
        {
            // ...new...cout..#.c
            size_t pos = s.find(sep);
            size_t i = 0;
            while (pos != std::string::npos)
            {
                if (i == pos)
                {
                    i += sep.size();
                }
                else
                {
                    result.push_back(s.substr(i, pos - i));
                    i = pos + 1;
                }
                pos = s.find(sep, i);
            }
            if (i < s.size())
                result.push_back(s.substr(i));
            return result.size();
        }
    };

    class UUID
    {
    public:
        static std::string uuid()
        {
            std::random_device rd;
            std::mt19937_64 generator(rd());
            std::uniform_int_distribution<int> distribution(0, 255);
            std::string s;
            char buf[128];
            for (int i = 0; i < 8; i++)
            {
                sprintf(buf, "%02x", distribution(generator));
                s += buf;
                if (i % 2 && i > 1)
                    s += '-';
            }
            static std::atomic<size_t> seq(1);
            for (int i = 7; i >= 0; i--)
            {
                sprintf(buf, "%02lx", (seq >> i * 8) & 0xff);
                s += buf;
                if (i == 6)
                    s += '-';
            }
            seq++;
            return s;
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string &filename) : _filename(filename) {}
        ~FileHelper() {}
        bool exist()
        {
            struct stat st;
            return stat(_filename.c_str(), &st) == 0;
        }
        size_t size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                return 0;
            }
            return st.st_size;
        }
        bool read(std::string &body)
        {
            size_t fsize = size();
            body.resize(fsize);
            return read(&body[0], 0, fsize);
        }
        bool read(char *body, size_t offset, size_t len)
        {
            std::ifstream ifs(_filename, std::ios_base::binary | std::ios_base::in);
            if (!ifs.is_open())
            {
                ELOG("%s 文件打开失败", _filename.c_str());
                return false;
            }
            ifs.seekg(offset, std::ios::beg);
            ifs.read(body, len);
            if (!ifs.good())
            {
                ELOG("%s 文件读取失败！！", _filename.c_str());
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        // 保证文件无内容
        bool write(const std::string &body)
        {
            return write(body.c_str(), 0, body.size());
        }
        bool write(const char *body, size_t offset, size_t len)
        {
            // ofstream 无法在文件中间写入一个比文件大的数据，fstream 可以
            std::fstream fs(_filename, std::ios_base::binary | std::ios_base::in | std::ios_base::out);
            if (!fs.is_open())
            {
                ELOG("%s 文件打开失败", _filename.c_str());
                return false;
            }
            fs.seekp(offset, std::ios::beg);
            fs.write(body, len);
            if (!fs.good())
            {
                ELOG("%s 文件写入失败！！", _filename.c_str());
                fs.close();
                return false;
            }
            fs.close();
            return true;
        }
        bool rename(const std::string &name)
        {
            return ::rename(_filename.c_str(), name.c_str()) == 0;
        }
        static bool createFile(const std::string &filename)
        {
            if(FileHelper(filename).exist()) return true;
            std::fstream fs(filename, std::ios_base::binary | std::ios_base::out);
            if (!fs.is_open())
            {
                ELOG("%s 文件创建失败", filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }
        static bool removeFile(const std::string &filename)
        {
            return ::remove(filename.c_str()) == 0;
        }
        static bool createDirectory(const std::string &path)
        {
            if(FileHelper(path).exist()) return true;
            size_t pos = 0, n = path.size();
            do
            {
                pos = path.find('/', pos + 1);
                if (pos > n) pos = n;
                std::string tmp = path.substr(0, pos);
                int ret = ::mkdir(tmp.c_str(), 0775);
                if (ret < 0)
                {
                    if (!(tmp == "." || errno == EEXIST))
                    {
                        ELOG("创建目录 %s 失败", path.substr(0, pos).c_str());
                        return false;
                    }
                }
            } while (pos < n);
            return true;
        }
        static bool removeDirectory(const std::string &path)
        {
            return system(("rm -rf " + path).c_str()) != -1;
        }
        static std::string parentDirectory(const std::string &filename)
        {
            size_t pos = filename.rfind('/');
            if (pos == std::string::npos)
            {
                // test.txt
                return "./";
            }
            return filename.substr(0, pos);
        }

    private:
        std::string _filename;
    };
}